/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.soa.util.wx;

import com.lianzt.commondata.AbstractCommonData;
import com.lianzt.commondata.CommonDataElement;
import com.lianzt.commondata.DataConvertFactory;
import com.lianzt.exception.InstanceDataException;
import com.lianzt.util.MD5;
import com.lianzt.util.StringUtil;
import com.soa.exception.GlobalException;
import com.soa.util.SystemUtil;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.annotation.Resource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 微信支付工具类
 *
 * @author zuotai
 */
@Deprecated
@Component
public class WxPayUtil {

    @Resource
    private WeixinUtil weixinUtil;
    /**
     * 支付下单
     */
    public final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    public final String TRADE_TYPE_JSAPI = "JSAPI";         //交易类型
    public final int TRADE_TIMEOUT = 600000;           //交易超时时间 10分钟
    private final Logger log = LoggerFactory.getLogger(WxPayUtil.class);

    /**
     * 调用微信支付API，会自动加入随机串与签名
     *
     * @param api
     * @param acd
     * @return
     */
    public AbstractCommonData callApi(String api, AbstractCommonData acd) {
        if (StringUtil.isNull(api) || acd == null || acd.isEmpty()) {
            return null;
        }
        try {
            javaSign(acd);      //增加签名
        } catch (UnsupportedEncodingException e) {
            log.error("对支付检验字段加密时出现异常：", e);
            throw new GlobalException(300004, e);      //请求wx-server超时
        }
        return callApi(api, data2Xml(acd));
    }

    /**
     * 调用微信支付API，不会加入随机串与签名
     *
     * @param api
     * @param acd
     * @return
     */
    public AbstractCommonData callApi(String api, String xml) {

        if (StringUtil.isNull(xml)) {
            return null;
        }
        URL url = null;
        URLConnection con = null;
        BufferedWriter bw = null;
        BufferedReader br = null;

        try {
            url = new URL(api);
            con = url.openConnection();
            /**
             * 然后把连接设为输出模式。URLConnection通常作为输入来使用，比如下载一个Web页。
             * 通过把URLConnection设为输出，你可以把数据向你个Web页传送。下面是如何做：
             */
            con.setDoOutput(true);
            con.setConnectTimeout(weixinUtil.TIMEOUT);
            con.setReadTimeout(weixinUtil.TIMEOUT);
            con.addRequestProperty("Content-Type", "application/xml");
            /**
             * 最后，为了得到OutputStream，简单起见，把它约束在Writer并且放入POST信息中，例如： ...
             */
            bw = new BufferedWriter(new OutputStreamWriter(con.getOutputStream(), "UTF-8"));
            StringBuilder sb = new StringBuilder();
            sb.append(xml);
            log.info("weixin pay api req : {}", xml);
            bw.write(sb.toString());

            bw.flush();

            String tmpStr;
            sb = new StringBuilder();

            br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
            while ((tmpStr = br.readLine()) != null) {
                sb.append(tmpStr);
            }
            log.info("weixin pay api res : {}", sb);
            AbstractCommonData res = xml2Data(sb.toString());       //把响应转为data
            if (res == null || !"SUCCESS".equals(res.getStringValue("return_code"))) {
                //异常
                throw new GlobalException(300004, res.getStringValue("return_code") + "-" + res.getStringValue("return_msg"));          //请求处理错误:!#!
            }
            return res;
        } catch (SocketTimeoutException e) {
            //超时
            throw new GlobalException(300001, e);      //请求wx-server超时
        } catch (IOException e) {
            //网络异常
            log.debug("网络异常:", e);
            throw new GlobalException(300002, e);      //wx-server连接异常
        } catch (DocumentException e) {
            //响应格式异常
            throw new GlobalException(300003, e);      //wx-server响应格式错误
        } finally {
            if (bw != null) {
                try {
                    bw.close();
                } catch (Exception e) {
                    log.warn("关闭连接异常：" + e.toString());
                }
            }
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                    log.warn("关闭连接异常：" + e.toString());
                }
            }
        }
    }

    /**
     * commondata 转 xml
     *
     * @param acd
     * @return
     */
    public String data2Xml(AbstractCommonData acd) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        for (Map.Entry<String, CommonDataElement> e : acd.entrySet()) {
            sb.append("<").append(e.getKey()).append(">");
            sb.append(e.getValue().getValue());
            sb.append("</").append(e.getKey()).append(">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * xml 转 commondata
     *
     * @param xml
     * @return
     */
    public AbstractCommonData xml2Data(String xml) throws DocumentException {
        Document document = new SAXReader().read(new StringReader(xml));
        Element root = document.getRootElement();
        if ("xml".equals(root.getName())) {
            Iterator<Element> iterator = root.elementIterator();
            AbstractCommonData acd = DataConvertFactory.getInstanceEmpty();
            while (iterator.hasNext()) {
                Element item = iterator.next();
                acd.putStringValue(item.getName(), item.getText());
            }
            return acd;
        } else {
            log.error("解析非法的微信xml");
            return null;
        }
    }

    /**
     * java端对请求报文签名
     *
     * @param acd
     */
    public void javaSign(AbstractCommonData acd) throws UnsupportedEncodingException {
        if (acd == null || acd.isEmpty()) {
            return;
        }
        acd.putStringValue("nonce_str", weixinUtil.getRandomStringByLength(32));     //放入随机数
        acd.putStringValue("sign", createSign(acd));
    }

    /**
     * 生成签名 先对包中的数据排序,返回生成的签名
     *
     * @param acd
     * @return
     */
    public String createSign(AbstractCommonData acd) throws UnsupportedEncodingException {
        List<String> keyList = new ArrayList<String>(acd.keySet());
        Collections.sort(keyList);      //对请求的参数名按字典顺序排序
        StringBuilder sb = new StringBuilder();
        for (String key : keyList) {
            if (StringUtil.notNull(acd.getStringValue(key))) {
                //不为空时才参与校验
                sb.append(key).append("=").append(acd.getStringValue(key)).append("&");
            }
        }
        sb.append("key=").append(SystemUtil.getSysConfig("wx_key"));
        log.info("支付检验字段：{}", sb);
        return MD5.digestAsHex(sb.toString().getBytes("utf-8")).toUpperCase();     //md5后转大写
    }

    /**
     * js端对请求报文签名
     *
     * @param acd
     */
    public void jsapiSign(AbstractCommonData acd) {
        if (acd == null || acd.isEmpty()) {
            return;
        }
        acd.putStringValue("signType", "MD5");
        acd.putStringValue("nonceStr", weixinUtil.getRandomStringByLength(32));     //放入随机数
        try {
            acd.putStringValue("paySign", createSign(acd));
        } catch (UnsupportedEncodingException e) {
            acd.putStringValue("paySign", "签名错误");
            log.error("微信api签名时出现异常:", e);
        }
    }

    /**
     * 检验签名是否正确,校验之后会删除sign项
     *
     * @param xml
     * @return
     */
    public boolean checkSign(AbstractCommonData xml) {
        if (xml == null || xml.isEmpty()) {
            return false;
        }
        String sign = xml.getStringValue("sign");
        xml.remove("sign");
        try {
            return sign.equals(createSign(xml));
        } catch (UnsupportedEncodingException e) {
            log.warn("检验微信api签名MD5操作时异常:", e);
            return false;
        }
    }

    /**
     * 生成调用微信支付js sdk的参数
     * @param prepayId 微信返回的prepay_id字段
     * @param timeStamp 发起交易时填写的时间，单位为秒
     * @return
     */
    public AbstractCommonData getJsapiPay(String prepayId, String timeStamp) {
        AbstractCommonData jsapiData = DataConvertFactory.getInstanceEmpty();
        jsapiData.putStringValue("appId", SystemUtil.getSysConfig("wx_appid"));
        jsapiData.putStringValue("timeStamp", timeStamp);       //时间截的单位为秒
        jsapiData.putStringValue("package", "prepay_id=" + prepayId);
        jsapiSign(jsapiData);
        jsapiData.put("timestamp", jsapiData.get("timeStamp"));      //jsapi调用时需要把 s 小写
        jsapiData.remove("timeStamp");
        return jsapiData;
    }
}
